Skip to content

Extract Tile selection algorithms from Tileset into free functions#1342

Open
calebbuffa wants to merge 31 commits into
CesiumGS:mainfrom
calebbuffa:ownership
Open

Extract Tile selection algorithms from Tileset into free functions#1342
calebbuffa wants to merge 31 commits into
CesiumGS:mainfrom
calebbuffa:ownership

Conversation

@calebbuffa
Copy link
Copy Markdown
Contributor

@calebbuffa calebbuffa commented Apr 9, 2026

Description

This PR breaks tile selection algorithms out of private methods in the Tileset class and into free functions. The purpose is to reduce the surface area of Tileset and make the selection algorithms independently testable.

Changes

1) — tileStateUpdater callback: decouple traversal from loading

The traversal helpers (_visitTileIfNeeded, etc.) previously called _pTilesetContentManager->updateTileContent() directly, creating a compile-time dependency on TilesetContentManager inside the selection algorithm. A std::function<void(Tile&)> tileStateUpdater field was added to TilesetFrameState. Tileset::updateViewGroup injects the implementation via a lambda; traversal calls the callback. The traversal has no #include dependency on TilesetContentManager.

Files: TilesetFrameState.h, Tileset.cpp

2) — selectTiles(): pure free function for tile LOD selection

The entire traversal algorithm (_visitTileIfNeeded, _visitTile, _renderLeaf, _renderInnerTile, _visitVisibleChildrenNearToFar, _kickDescendantsAndRenderTile, _loadAndRenderAdditiveRefinedTile, frustum/fog cull, occlusion check, SSE computation) was extracted from Tileset private methods into free functions in a new translation unit TilesetSelection.cpp. The public entry point is:

selectTiles(
    TileSelectionContext& context,   // options + externals + scratch buffers
    TilesetFrameState& frameState,
    Tile& rootTile,
    ViewUpdateResult& result,
);

Tileset::updateViewGroup is now a thin coordinator: it builds frameState and calls selectTiles(). The private traversal method declarations and TraversalDetails/CullResult structs are gone from Tileset.h.

Files: TilesetSelection.h (new), TilesetSelection.cpp (new), Tileset.h, Tileset.cpp


Issue number or link

N/A.


Author checklist

  • I have submitted a Contributor License Agreement (only needed once).
  • I have done a full self-review of my code.
  • I have updated CHANGES.md with a short summary of my change (for user-facing changes).
  • I have added or updated unit tests to ensure consistent code coverage as necessary.
  • I have updated the documentation as necessary.

Testing plan

Regression: All existing tests in TestTilesetSelectionAlgorithm.cpp exercise the full selection pipeline via Tileset::updateViewGroup, which now delegates to selectTiles(). These cover: replace/additive refinement, SSE thresholds, frustum/fog culling, multi-frustum, forbidHoles, LOD transition fade-out, unconditionallyRefine.

New isolation test (TestTilesetSelection.cpp):

  • selectTiles is callable as a free function — constructs a minimal EllipsoidTilesetLoader-backed tileset, calls selectTiles() directly (bypassing Tileset::updateViewGroup), and asserts at least one tile is selected with no tiles kicked.
  • selectTiles result matches updateViewGroup result — runs both updateViewGroup and selectTiles() on the same warmed-up tileset and asserts tile render count and visit count are identical between the two paths.

Build verification: cmake --build build --target Cesium3DTilesSelection succeeds with zero errors.


Remaining Tasks

Things that could be done:

  • TilesetViewGroup::startNewFrame/finishFrame still take const Tileset&, which prevents fully async-free unit tests of selectTiles. Decoupling that is a natural follow-up once this PR lands.

@calebbuffa calebbuffa marked this pull request as ready for review April 9, 2026 21:18
@calebbuffa calebbuffa changed the title Ownership Improve Ownership Semantics Apr 9, 2026
@calebbuffa calebbuffa changed the title Improve Ownership Semantics Improve Ownership Semantics in Cesium3DTilesSelection Apr 10, 2026
Copy link
Copy Markdown
Member

@kring kring left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR @calebbuffa!

I love the way you've broken the tileset selection algorithm into a separate, testable function.

Some of the other changes seem mostly unrelated to this important core, though, and I have some quibbles with them. See below.

Comment thread Cesium3DTilesSelection/include/Cesium3DTilesSelection/Tile.h Outdated
Comment thread Cesium3DTilesSelection/include/Cesium3DTilesSelection/Tile.h Outdated
Comment thread Cesium3DTilesSelection/include/Cesium3DTilesSelection/ThreadSafety.h Outdated
Comment thread Cesium3DTilesSelection/include/Cesium3DTilesSelection/TileHierarchy.h Outdated
Comment thread Cesium3DTilesSelection/include/Cesium3DTilesSelection/TilesetSelection.h Outdated
*
* Tiles at the head are least-recently-used; tiles at the tail are
* most-recently-used. All methods must be called from the main thread.
* [main-thread-only]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this class plays by our thread safety rules just fine. There's no need to further restrict it to "main thread only".
https://cesium.com/learn/cesium-native/ref-doc/thread-safety-rules.html

Comment thread Cesium3DTilesSelection/include/Cesium3DTilesSelection/TileUnloadQueue.h Outdated
Comment thread Cesium3DTilesSelection/src/TileOverlaySystem.h Outdated
Comment thread Cesium3DTilesSelection/src/TilesetSelection.cpp Outdated
Comment thread Cesium3DTilesSelection/src/TilesetSelection.cpp
@calebbuffa
Copy link
Copy Markdown
Contributor Author

calebbuffa commented Apr 13, 2026

@kring thank you for the feedback! I got a little too carried away. I stripped the changes just to selectTiles and the TilesUnloadQueue. Thanks for refocusing!

@calebbuffa calebbuffa changed the title Improve Ownership Semantics in Cesium3DTilesSelection Extract Tile selection algorithms from Tileset into free functions Apr 13, 2026
@j9liu j9liu requested a review from kring April 16, 2026 19:23
@j9liu j9liu added this to the May 2026 Release milestone Apr 17, 2026
@kring
Copy link
Copy Markdown
Member

kring commented Apr 20, 2026

I asked Copilot to review this PR, specifically looking for any changes that were inadvertently introduced when
moving code from Tileset.cpp to TilesetSelection.cpp. Both observations look correct to me. However, I suggest moving the orphaned comment into the new file instead of deleting it.


I reviewed the code moved from Tileset.cpp to TilesetSelection.cpp for inadvertent changes. The core traversal logic appears to be a faithful move — no inadvertent behavioral differences found.

Two issues:

1. Orphaned doc comment in Tileset.h

When _loadAndRenderAdditiveRefinedTile was removed, its full /** ... */ doc comment was accidentally left behind. It now sits immediately before _unloadCachedTiles, making it appear to document that function:

private:
  /**
   * @brief When called on an additive-refined tile, queues it for load and adds
   * it to the render list.
   * ...
   * @return false The non-additive-refined tile was ignored.
   */
  // ← _loadAndRenderAdditiveRefinedTile declaration was deleted, but this comment wasn't

  void _unloadCachedTiles(double timeBudget) noexcept;

This happened because the deletion straddled a diff hunk boundary — the /** and @brief lines fell in the first hunk, the closing @param/@return/*/ lines in the second. The function declaration was correctly deleted but the entire comment body survived as context lines in both hunks.

Fix: delete the orphaned /** ... */ block from Tileset.h.

2. Unnecessary const_cast introduced in checkOcclusion

In TilesetSelection.cpp, the new checkOcclusion accesses ctx.childOcclusionProxies via a spurious const_cast:

auto& childOcclusionProxies =
    const_cast<std::vector<const TileOcclusionRendererProxy*>&>(
        ctx.childOcclusionProxies);

childOcclusionProxies is declared as std::vector<const TileOcclusionRendererProxy*>& (a non-const reference member) in TraversalContext. Reference members retain their mutability when accessed through a const struct reference in C++, so the cast is unnecessary. The original _checkOcclusion in Tileset.cpp modified this->_childOcclusionProxies directly without any cast.

Fix: remove the const_cast and use ctx.childOcclusionProxies directly.

Copy link
Copy Markdown
Member

@kring kring left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few more comments. Other than this, this PR looks good to me! I'll let @azrogers or @j9liu merge it, though.

Comment thread Cesium3DTilesSelection/src/Tileset.cpp Outdated
Comment thread Cesium3DTilesSelection/src/Tileset.cpp Outdated
Comment thread Cesium3DTilesSelection/src/Tileset.cpp Outdated
@j9liu
Copy link
Copy Markdown
Contributor

j9liu commented Apr 22, 2026

Hi @calebbuffa! We're interested in getting this into the next release, are you able to address Kevin's comments above within the next few days?

@calebbuffa
Copy link
Copy Markdown
Contributor Author

@j9liu yes! Sorry got a little sidetracked the last few days. I'll fix the PR by end of week!

@j9liu j9liu removed this from the May 2026 Release milestone Apr 27, 2026
@j9liu j9liu requested review from Copilot and j9liu April 27, 2026 15:07
@j9liu
Copy link
Copy Markdown
Contributor

j9liu commented Apr 27, 2026

Thanks for the updates @calebbuffa! Just for transparency, I think I'll actually defer merging this to after our May 1 release if that's alright. We'll still review this soon, but it would help to have additional time to stress-test the changes, just in case there are any surprises in our runtimes.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors the tileset LOD / tile-selection traversal by extracting it from Tileset private methods into a standalone selectTiles() free function, aiming to reduce Tileset surface area and make selection logic more directly testable.

Changes:

  • Introduces selectTiles() and TileSelectionContext as a public entry point for the selection traversal.
  • Adds TilesetFrameState::tileStateUpdater to decouple traversal from TilesetContentManager.
  • Refactors tile content eviction tracking behind a new TileUnloadQueue wrapper and adds new unit tests for the free-function selection path.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
Cesium3DTilesSelection/test/TestTilesetSelection.cpp Adds unit tests that call selectTiles() directly and compare results to Tileset::updateViewGroup.
Cesium3DTilesSelection/src/TilesetSelection.cpp New implementation of the extracted tile-selection / traversal algorithm.
Cesium3DTilesSelection/src/Tileset.cpp Makes updateViewGroup a coordinator that builds frame state/context and calls selectTiles().
Cesium3DTilesSelection/include/Cesium3DTilesSelection/TilesetSelection.h New public API header defining TileSelectionContext and selectTiles().
Cesium3DTilesSelection/include/Cesium3DTilesSelection/TilesetFrameState.h Adds tileStateUpdater callback for per-visited-tile state advancement.
Cesium3DTilesSelection/include/Cesium3DTilesSelection/Tileset.h Removes private traversal helpers and includes the new selection header.
Cesium3DTilesSelection/src/TilesetContentManager.h Replaces raw unused-tile linked list member with TileUnloadQueue.
Cesium3DTilesSelection/src/TilesetContentManager.cpp Switches eviction bookkeeping to TileUnloadQueue and adjusts member initialization.
Cesium3DTilesSelection/include/Cesium3DTilesSelection/TileUnloadQueue.h Adds a small wrapper around Tile::UnusedLinkedList for LRU eviction tracking.
Cesium3DTilesSelection/include/Cesium3DTilesSelection/Tile.h Minor doc wording tweak for getParent() comment.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread Cesium3DTilesSelection/src/TilesetSelection.cpp
Comment thread Cesium3DTilesSelection/src/TilesetSelection.cpp Outdated
Comment thread Cesium3DTilesSelection/src/TilesetSelection.cpp
Copy link
Copy Markdown
Contributor

@j9liu j9liu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the work on this PR @calebbuffa! It might look like there's a lot of comments but I promise they're all minor.

The biggest note is that we lost a lot of helpful internal comments that we should preserve for future knowledge. Otherwise, it's just a matter of style / minor tweaks.

Comment thread Cesium3DTilesSelection/src/TilesetSelection.cpp
Comment thread Cesium3DTilesSelection/include/Cesium3DTilesSelection/TilesetSelection.h Outdated
Comment thread Cesium3DTilesSelection/include/Cesium3DTilesSelection/TileUnloadQueue.h Outdated
Comment thread Cesium3DTilesSelection/src/Tileset.cpp
Comment thread Cesium3DTilesSelection/src/TilesetSelection.cpp
Comment thread Cesium3DTilesSelection/src/TilesetSelection.cpp
Comment thread Cesium3DTilesSelection/src/TilesetSelection.cpp Outdated
Comment thread Cesium3DTilesSelection/src/TilesetSelection.cpp
@j9liu j9liu added this to the June 2026 Release milestone Apr 28, 2026
@calebbuffa calebbuffa requested a review from j9liu May 12, 2026 21:58
@calebbuffa
Copy link
Copy Markdown
Contributor Author

calebbuffa commented May 12, 2026

@j9liu thank you for the feedback! I believe I fixed all of your comments - but please let me know if I missed any!

I mangled one of the files in git diffs accidentally it looks like, I'm reverting the changes and will re-update.

@javagl
Copy link
Copy Markdown
Contributor

javagl commented May 18, 2026

Stumbling over this, it looks like it addresses #36 to some extent, alongside some of the suggestions from #126 (even though that suggested (pseudocode) separation of TraversealState and TraversalResult seems to be reflected in the TileSelectionContext here)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants